/**
  * @File: ClipboardMonitor.cpp 
  *
  * @Author: Bob 
  *
  * @Date: 2016/09/08 
  *
  * @Version: 1.0
  *
  * @Last Modify By: Bob
  *
  * @Last Modify Time: 2016/09/08 
  *
  * @Description:
  *		 剪贴板监控程序
  *
  */

#define _CRT_SECURE_NO_WARNINGS

#include "ClipboardMonitor.h"
#include <iostream>
#include <tchar.h>
#include <Psapi.h>
#include <objbase.h>

ClipboardMonitor::ClipboardMonitor()
{

}


ClipboardMonitor::~ClipboardMonitor()
{

}


ClipboardMonitor* ClipboardMonitor::GetInstance()
{
	static ClipboardMonitor clipboardMonitor;
	return &clipboardMonitor;
}


void ClipboardMonitor::Init()
{
	CreateADaemoWindow();
}


BOOL ClipboardMonitor::CreateADaemoWindow()
{
	TCHAR szWindowClassName[] = "SyniWindows";
	WNDCLASS windowClass;
	BOOL bRet = FALSE;
	DWORD dwErrorCode = 0;

	bRet = GetClassInfo(NULL, szWindowClassName, &windowClass);
	if (!bRet)
	{
		memset(&windowClass, 0, sizeof(WNDCLASS));
		windowClass.lpfnWndProc = DefWindowProc;
		windowClass.cbWndExtra = 0;
		windowClass.hInstance = NULL;
		windowClass.lpszClassName = szWindowClassName;

		bRet = RegisterClass(&windowClass);
		if (!bRet)
		{
			dwErrorCode = GetLastError();
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "RegisterClass Failed, ErrorCode:" << dwErrorCode << std::endl;
			return FALSE;
		}
	}
	else
	{
		dwErrorCode = GetLastError();
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "GetClassInfo Failed, ErrorCode:" << dwErrorCode << std::endl;
		return FALSE;
	}

	HWND hWnd = CreateWindow(szWindowClassName, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
	if (hWnd == NULL)
	{
		dwErrorCode = GetLastError();
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "CreateWindow Failed" << std::endl;
		return FALSE;
	}

	bRet = SetWindowLongPtr(hWnd, GWL_WNDPROC, reinterpret_cast <LONG_PTR> (ClipboardMonitor::DaemoWindowDlgProc));
	if (!bRet)
	{
		dwErrorCode = GetLastError();
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "SetWindowLongPtr Failed " << std::endl;
		return FALSE;
	}

	// 将hWnd加入一个窗口链，每当剪贴板的内容发生变化时，就会通知这个窗口。
	::SetClipboardViewer(hWnd);

	// 窗口消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return TRUE;
}


BOOL ClipboardMonitor::IsClipboardOwnerMySelf(HWND hMySelfWnd, HWND hClipboardWnd)
{
	std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << std::endl;
	std::cout << "[hMySelfWnd]:-----" << hMySelfWnd << std::endl;
	std::cout << "[hClipboardWnd]:--" << hClipboardWnd << std::endl;

	return (hMySelfWnd == hClipboardWnd);
}


BOOL CALLBACK ClipboardMonitor::DaemoWindowDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
	HWND hClipboardOwnerWnd = NULL;
	BOOL bSelf = FALSE;

	switch (uMessage)
	{
		case WM_DRAWCLIPBOARD:
		{
			hClipboardOwnerWnd = ::GetClipboardOwner();
			bSelf = ClipboardMonitorInstance->IsClipboardOwnerMySelf(hDlg, hClipboardOwnerWnd);
			if (!bSelf)
			{
				if (::OpenClipboard(hDlg))
				{
					ClipboardMonitorInstance->SaveDataToFile(hClipboardOwnerWnd);
					::CloseClipboard();
				}
			}
		}

		default:
				break;
	}

	return TRUE;
}


std::string ClipboardMonitor::GenerateFileName(UINT uClipboardDataFromat)
{
	GUID guid;
	char szBuf[64] = { 0 };
	std::string strFileName = "";

	CoCreateGuid(&guid);
	_snprintf_s(szBuf, sizeof(szBuf),
		"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
		guid.Data1, guid.Data2, guid.Data3,
		guid.Data4[0], guid.Data4[1],
		guid.Data4[2], guid.Data4[3],
		guid.Data4[4], guid.Data4[5],
		guid.Data4[6], guid.Data4[7]);

	strFileName = szBuf;
	if (uClipboardDataFromat == CF_TEXT)
	{
		strFileName += TXT_FILE_NAME;
	}
	else if (uClipboardDataFromat == CF_BITMAP || uClipboardDataFromat == CF_DIB)
	{
		strFileName += BMP_FILE_NAME;
	}
	else if (uClipboardDataFromat == CF_UNICODETEXT)
	{
		//TODO
	}

	return strFileName;
}


std::string ClipboardMonitor::GetApplicationExName(HWND hWnd)
{
	std::string strApplicationExName = "";
	if (hWnd)
	{
		DWORD dwProcessID = 0;
		HMODULE hMod = NULL;
		::GetWindowThreadProcessId(hWnd, &dwProcessID);
		HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);

		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "dwProcessID: " << dwProcessID << std::endl;
		if (hProcess)
		{
			TCHAR szExFileName[MAX_PATH] = { 0 };
			::GetModuleFileNameEx(hProcess, hMod, szExFileName, MAX_PATH);
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "szExFileName: " << szExFileName << std::endl;
			strApplicationExName = szExFileName;
		}
		else
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "OpenProcess failed: " << std::endl;
		}
		::CloseHandle(hProcess);
	}
	return strApplicationExName;
}


BOOL ClipboardMonitor::SaveTextDataToFile(UINT uClipboardDataFromat, const char* pFilePath)
{
	BOOL bRet = false;

	do
	{
		HGLOBAL hGlobalMemory = ::GetClipboardData(uClipboardDataFromat);
		if (hGlobalMemory == NULL)
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "GetClipboardData failed" << std::endl;
			break;
		}

		DWORD dwDataSize = ::GlobalSize(hGlobalMemory);
		char* pClipboardData = (char*)::GlobalLock(hGlobalMemory);
		if (pClipboardData != NULL)
		{
			bRet = WriteTextFile(pClipboardData, dwDataSize, pFilePath);
		}
		else
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "GlobalLock failed" << std::endl;
		}
		::GlobalUnlock(hGlobalMemory);

	} while (false);

	return bRet;
}


BOOL ClipboardMonitor::WriteTextFile(char* pContent, DWORD dwSize, const char* pFilePath)
{
	BOOL bRet = FALSE;

	do
	{
		if (pContent == NULL || pFilePath == NULL)
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "pContent or pFilePath is NULL" << std::endl;
			break;
		}

		FILE *pFile = fopen(pFilePath, "w+");
		if (pFile == NULL)
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "file open failed" << std::endl;
			break;
		}

		fwrite(pContent, dwSize - 1, 1, pFile);
		if (pFile != NULL)
		{
			fclose(pFile);
			pFile = NULL;
		}
		bRet = TRUE;

	} while (FALSE);

	return bRet;
}


BOOL ClipboardMonitor::WriteBMPDateToFile(HBITMAP hBitmap, const char* pFilePath)
{
	if (hBitmap == NULL || pFilePath == NULL)
	{
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " hBitmap is NULL or szFileName is NULL" << std::endl;
		return FALSE;
	}

	BITMAP tmpBitmap;
	long nBmpPixel = 0; // 像素
	long nBmpWidth = 0;
	long nBmpHeight = 0;
	long nBmpSize = 0;

	int nRetValue = ::GetObject(hBitmap, sizeof(tmpBitmap), &tmpBitmap);
	if (nRetValue)
	{
		nBmpWidth = tmpBitmap.bmWidth;
		nBmpHeight = tmpBitmap.bmHeight;
		nBmpPixel = tmpBitmap.bmBitsPixel; // 计算出像素的bit数
		nBmpSize = tmpBitmap.bmWidth * tmpBitmap.bmHeight * (tmpBitmap.bmBitsPixel >> 3);
		tmpBitmap.bmBits = (void *) new BYTE[nBmpSize];
		if (tmpBitmap.bmBits == NULL)
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " tmpBitmap.bmBits is NULL" << std::endl;
			return FALSE;
		}

		nRetValue = GetBitmapBits(hBitmap, nBmpSize, tmpBitmap.bmBits);
		if (!nRetValue)
		{
			delete tmpBitmap.bmBits;
			tmpBitmap.bmBits = NULL;
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " GetBitmapBits failed" << std::endl;
			return FALSE;
		}
	}
	else
	{
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " GetObject failed" << std::endl;
		return FALSE;
	}

	DWORD *lp_dwCanvas = new DWORD[nBmpWidth * nBmpHeight];
	if (lp_dwCanvas == NULL)
	{
		if (tmpBitmap.bmBits != NULL)
		{
			delete tmpBitmap.bmBits;
			tmpBitmap.bmBits = NULL;
		}
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " new lp_dwCanvas failed" << std::endl;
		return FALSE;
	}

	if (nBmpPixel == BMP_PIXEL_32)
	{
		for (long nHeight = 0; nHeight < nBmpHeight; nHeight++)
		{
			for (long nWidth = 0; nWidth < nBmpWidth; nWidth++)
			{
				RGBQUAD *rgb = ((RGBQUAD *)((char*)(tmpBitmap.bmBits) + tmpBitmap.bmWidthBytes*nHeight + nWidth*sizeof(DWORD)));
				lp_dwCanvas[(nBmpHeight - nHeight - 1)*nBmpWidth + nWidth] = *((DWORD *)rgb);
			}
		}
	}
	else if (nBmpPixel == BMP_PIXEL_24)
	{
		for (long nHeight = 0; nHeight < nBmpHeight; nHeight++)
		{
			for (long nWidth = 0; nWidth < nBmpWidth; nWidth++)
			{
				RGBTRIPLE rgbTriple = *((RGBTRIPLE *)((char*)(tmpBitmap.bmBits) + tmpBitmap.bmWidthBytes*nHeight + nWidth * 3));
				RGBQUAD rgbQuad;
				rgbQuad.rgbRed = rgbTriple.rgbtRed;
				rgbQuad.rgbGreen = rgbTriple.rgbtGreen;
				rgbQuad.rgbBlue = rgbTriple.rgbtBlue;
				lp_dwCanvas[(nBmpHeight - nHeight - 1)*nBmpWidth + nWidth] = *((DWORD *)(&rgbTriple));
			}
		}
	}
	else
	{
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " other bmp pixel" << std::endl;
	}

	WORD nBits = BMP_PIXEL_32;
	FILE *pFile = fopen(pFilePath, "wb");
	if (pFile == NULL)
	{
		if (tmpBitmap.bmBits != NULL)
		{
			delete tmpBitmap.bmBits;
			tmpBitmap.bmBits = NULL;
		}

		if (lp_dwCanvas != NULL)
		{
			delete lp_dwCanvas;
			lp_dwCanvas = NULL;
		}

		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << " File open failed" << std::endl;
		return FALSE;
	}

	// save bitmap file header 
	BITMAPFILEHEADER bmpFileHeader;
	bmpFileHeader.bfType = 0x4d42; // 位图文件类型，必须是0x4D42，即字符串“BM”，也就是说，所有的“*.bmp”文件的头两个字节都是“BM”。
	bmpFileHeader.bfSize = 0;
	bmpFileHeader.bfReserved1 = 0;
	bmpFileHeader.bfReserved2 = 0;
	bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
	fwrite((char*)&bmpFileHeader, sizeof(bmpFileHeader), 1, pFile);

	// save bitmap info header 
	BITMAPINFOHEADER bmpInfoHeader;
	bmpInfoHeader.biSize = sizeof(bmpInfoHeader);
	bmpInfoHeader.biWidth = nBmpWidth;
	bmpInfoHeader.biHeight = nBmpHeight;
	bmpInfoHeader.biPlanes = 1; //必须是1
	bmpInfoHeader.biBitCount = nBits;
	bmpInfoHeader.biCompression = BI_RGB; // BI_RGB表示未压缩
	bmpInfoHeader.biSizeImage = 0;
	bmpInfoHeader.biXPelsPerMeter = 0;
	bmpInfoHeader.biYPelsPerMeter = 0;
	bmpInfoHeader.biClrUsed = 0;
	bmpInfoHeader.biClrImportant = 0;

	fwrite((char*)&bmpInfoHeader, sizeof(bmpInfoHeader), 1, pFile);
	fwrite((char*)lp_dwCanvas, 1, (nBits >> 3) * nBmpWidth * nBmpHeight, pFile);

	if (pFile != NULL)
	{
		fclose(pFile);
		pFile = NULL;
	}

	if (tmpBitmap.bmBits != NULL)
	{
		delete tmpBitmap.bmBits;
		tmpBitmap.bmBits = NULL;
	}

	if (lp_dwCanvas != NULL)
	{
		delete lp_dwCanvas;
		lp_dwCanvas = NULL;
	}

	return TRUE;
}


BOOL ClipboardMonitor::SaveBMPDataToFile(UINT uClipboardDataFromat, const char* pFilePath)
{
	BOOL bRet = FALSE;

	do
	{
		if (pFilePath == NULL)
		{
			std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "pFilePath is NULL" << std::endl;
			break;
		}

		HBITMAP hBitmap = (HBITMAP)::GetClipboardData(uClipboardDataFromat);
		if (hBitmap != NULL)
		{
			GlobalLock(hBitmap);
			bRet = WriteBMPDateToFile(hBitmap, pFilePath);
			GlobalUnlock(hBitmap);
		}

	} while (FALSE);

	return bRet;
}


BOOL ClipboardMonitor::SaveDataToFile(HWND hClipboardOwnerWnd)
{
	if (hClipboardOwnerWnd == NULL)
	{
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "hClipboardOwnerWnd is NULL" << std::endl;
	}

	BOOL bRet = FALSE;
	if (::IsClipboardFormatAvailable(CF_TEXT))
	{
		std::string strFilePath = GenerateFileName(CF_TEXT);
		std::string strApplicationExName = GetApplicationExName(hClipboardOwnerWnd);
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "strFilePath: " << strFilePath.c_str() << " strApplicationExName: " << strApplicationExName.c_str() << std::endl;
		bRet = SaveTextDataToFile(CF_TEXT, strFilePath.c_str());
	}
	else if (::IsClipboardFormatAvailable(CF_UNICODETEXT))
	{
		// TODO:
	}
	else if (::IsClipboardFormatAvailable(CF_BITMAP))
	{
		std::string strFilePath = GenerateFileName(CF_BITMAP);
		std::string strApplicationExName = GetApplicationExName(hClipboardOwnerWnd);
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "strFilePath: " << strFilePath.c_str() << " strApplicationExName: " << strApplicationExName.c_str() << std::endl;
		bRet = SaveBMPDataToFile(CF_BITMAP, strFilePath.c_str());
	}
	else if (::IsClipboardFormatAvailable(CF_DIB))
	{
		std::string strFilePath = GenerateFileName(CF_DIB);
		std::string strApplicationExName = GetApplicationExName(hClipboardOwnerWnd);
		std::cout << "[" << __FUNCTION__ << ":" << __LINE__ << "] " << "strFilePath: " << strFilePath.c_str() << " strApplicationExName: " << strApplicationExName.c_str() << std::endl;
		bRet = SaveBMPDataToFile(CF_DIB, strFilePath.c_str());
	}
	return bRet;
}